#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#include <open62541/client_subscriptions.h>

#include <signal.h>
#include <stdlib.h>
#include <iostream>

/* 存储变量的服务端例程序 */

static void stopHandler(int sig);
static void addVariable(UA_Server *server);
static void addMatrixVariable(UA_Server *server);
static void addMonitoredItemToCurrentTimeVariable(UA_Server *server);

static volatile UA_Boolean running = true;

int main(void)
{
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);

    //创建服务器
    UA_Server *server = UA_Server_new();

    //获取端口4840上的默认配置;
    UA_ServerConfig *config = UA_Server_getConfig(server);
    //在端口4840应用默认配置
    UA_ServerConfig_setDefault(config);

    addVariable(server);
    addMatrixVariable(server);
    //添加变量监控程序
    // addMonitoredItemToCurrentTimeVariable(server);
    //运行服务器，进行事件循环， 当runing 为false时解除阻塞;
    UA_StatusCode retval = UA_Server_run(server, &running);
    //服务器下线
    UA_Server_delete(server);
    return retval = UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

static void stopHandler(int sig)
{
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "received ctrl-c");
    running = false;
}

UA_Int32 source_data = 0;
static UA_StatusCode readVariable(UA_Server *server,
                                  const UA_NodeId *sessionId, void *sessionContext,
                                  const UA_NodeId *nodeId, void *nodeContext,
                                  UA_Boolean sourceTimeStamp, const UA_NumericRange *range,
                                  UA_DataValue *dataValue)
{
    std::cout << "read data from server, node id:  " << nodeId->identifier.string.data << std::endl;
    //模拟数据接收过程
    UA_Int32 current_value = source_data++;
    UA_StatusCode ret = UA_Variant_setScalarCopy(&dataValue->value, &current_value, &UA_TYPES[UA_TYPES_INT32]);
    if (ret == UA_STATUSCODE_GOOD)
    {
        std::cout << "读取数据值: " << source_data << std::endl;
        dataValue->hasValue = true;
        return UA_STATUSCODE_GOOD;
    }
    else
    {
        std::cout << "读取失败: " << std::endl;
        return UA_STATUSCODE_BADENCODINGERROR;
    }
}

static UA_StatusCode writeVariable(UA_Server *server,
                                   const UA_NodeId *sessionId, void *sessionContext,
                                   const UA_NodeId *nodeId, void *nodeContext,
                                   const UA_NumericRange *range, const UA_DataValue *data)
{
    std::cout << "write variable node id: " << nodeId->identifier.string.data << std::endl;
    if (data->hasValue)
    {

        source_data = *(UA_Int32 *)data->value.data;
        std::cout << "要写入的数据是： " << source_data << std::endl;
    }
    return UA_STATUSCODE_GOOD;
}

/*向服务器添加变量*/
static void addVariable(UA_Server *server)
{
    //指定变量的属性；
    UA_VariableAttributes attr = UA_VariableAttributes_default;

    //将attr的值绑定到myInteger

    attr.description = UA_LOCALIZEDTEXT("en-US", "the answer");            //指定变量属性描述
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");            //指定变量属性的显示名
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;                       //指定变量属性的数据类型
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE; //指定变量的读写权限

    /* Add the variable node to the information model */
    UA_NodeId var_id = UA_NODEID_STRING(1, "the.answer");                        //初始化节点ID(通过字符串，由命名空间索引与字符串标识组成)
    UA_QualifiedName var_name = UA_QUALIFIEDNAME(1, "the answer");               //初始化节点名
    UA_NodeId var_parent_id = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);      //初始化父节点ID（由命名空间索引与数字标识组成），数字标识为对象文件夹
    UA_NodeId var_parent_ref_id = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);      //初始化父节点引用，数字标识指定为UA_NS0ID_ORGANIZES
    UA_NodeId var_type_id = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE); //初始化变量类型ID，数字标识指定为基础类型宏

    //指定数据源
    UA_DataSource dataSource;
    dataSource.read = readVariable;   //指定读取数据的回调
    dataSource.write = writeVariable; //指定写入数据的回调

    //服务器通过数据源方式添加变量节点，并绑定节点到相关属性节点
    UA_StatusCode ret = UA_Server_addDataSourceVariableNode(server, var_id, var_parent_id, var_parent_ref_id,
                                                            var_name, var_type_id, attr, dataSource, NULL, NULL);
    if (ret == UA_STATUSCODE_GOOD)
    {
        std::cout << "添加成功" << std::endl;
    }
    else
    {
        std::cout << "添加失败" << std::endl;
    }
    //直接添加变量节点
    // UA_Int32 myInteger = 49;
    //	 UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    //    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
    //                              parentReferenceNodeId, myIntegerName,
    //                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}

static void addMatrixVariable(UA_Server *server)
{
    //声明默认数据结构类型的变量；
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "Double Matrix");
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    /* Set the variable value constraints */
    attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
    //指定attr的值的排列行数
    attr.valueRank = UA_VALUERANK_TWO_DIMENSIONS;
    UA_UInt32 arrayDims[2] = {2, 2};
    //指定attr为两行两列
    attr.arrayDimensions = arrayDims;
    attr.arrayDimensionsSize = 2;

    /* Set the value. The array dimensions need to be the same for the value. */
    UA_Double zero[4] = {0.1, 0.2, 0.0, 0.0};
    //将attr的值绑定到myzero
    UA_Variant_setArray(&attr.value, zero, 4, &UA_TYPES[UA_TYPES_DOUBLE]);
    attr.value.arrayDimensions = arrayDims;
    attr.value.arrayDimensionsSize = 2;

    char node_id[] = "double.matrix";
    char node_name[] = "double.matrix";
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, node_id);
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, node_name);
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                              attr, NULL, NULL);
}

static void dataChangeNotificationCallback(UA_Server *server, UA_UInt32 monitoredItemId,
                                           void *monitoredItemContext, const UA_NodeId *nodeId,
                                           void *nodeContext, UA_UInt32 attributeId,
                                           const UA_DataValue *value)
{
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Received Notification");
}

/*添加变量监控，当变量改变时调用回调函数*/
static void addMonitoredItemToCurrentTimeVariable(UA_Server *server)
{
    // UA_NodeId currentTimeNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
    char node_id[] = "the.answer";
    UA_NodeId currentTimeNodeId = UA_NODEID_STRING(1, node_id);
    UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(currentTimeNodeId);
    monRequest.requestedParameters.samplingInterval = 1000.0; /* 100 ms interval */
    UA_Server_createDataChangeMonitoredItem(server, UA_TIMESTAMPSTORETURN_SOURCE,
                                            monRequest, NULL, dataChangeNotificationCallback);
}
